import { isFunction } from "../utils";

export type DepsMap = Map<unknown, Set<ReactiveEffect>>
export interface ReactiveEffect<T = any> {
    (...args: any[]): T
    _isEffect: true
    raw: () => T // 原函数
    iterate?: boolean // 改方法是否存入ITERATE_KEY
    deps?: Set<ReactiveEffect>[],
    stop?: () => void
}

let effectStack: ReactiveEffect[] = []; // 适应多层effect
let activeEffect: ReactiveEffect; // 当前存储的effect方法
export const ITERATE_KEY = Symbol('')
export function isEffect(fn: any): fn is ReactiveEffect {
    return fn && fn._isEffect === true
}
/** 
 * 创建Deps依赖 
 * @return {DepsMap} Deps依赖
 */
export const createDepsMap = (): DepsMap => new Map()

/** 
 * 创建effect
 * @param fn {Function} 要观察的方法
 * @param iterate {Boolean} 是否监听根
 * @return {ReactiveEffect} 返回effect
 */
export function effect<T = any>(
    fn: () => T,
    iterate: boolean = false
): ReactiveEffect<T> {
    if (isEffect(fn)) {
        fn = fn.raw
    }
    const effect = createReactiveEffect(fn, iterate)
    effect()
    return effect
}
/** 
 * 创建effect
 * @param fn {Function} 要观察的方法
 * @param iterate {Boolean} 是否监听根
 * @return {ReactiveEffect} 返回effect
 */
export function createReactiveEffect<T extends any>(
    fn: (...args: any[]) => T,
    iterate: boolean = false
): ReactiveEffect<T> {
    const effect = function reactiveEffect(...args: unknown[]): unknown {
        try {
            effectStack.push(effect)
            activeEffect = effect
            return fn(...args)
        } finally {
            effectStack.pop()
            activeEffect = effectStack[effectStack.length - 1];
        }
    } as ReactiveEffect
    effect.raw = fn
    effect._isEffect = true
    effect.iterate = iterate
    effect.deps = []
    effect.stop = () => { effect.deps.forEach(dep => dep.delete(effect)) };
    return effect
}

/** 
 * 值获取，获取effect存入Deps中
 * @param depsMap {Map} Deps依赖
 * @param key {unknow} 键值
 */
export function track(depsMap: DepsMap, key: unknown) {
    // effect不存在，不track
    if (activeEffect === undefined) return

    // 如果是iterate，存入ITERATE_KEY
    activeEffect.iterate && addDeps(depsMap, ITERATE_KEY, activeEffect)

    addDeps(depsMap, key, activeEffect)
}

/** 
 * 值改变，执行Deps中对应键值的函数
 * @param depsMap {Map} Deps依赖
 * @param key {unknow} 键值
 * @param add {Boolean} 是否是新增属性
 */
export function trigger(depsMap: DepsMap, key: unknown, isAdd: boolean = false) {
    // 添加时，将ITERATE_KEY中的deps复制进新属性中
    isAdd && addDeps(depsMap, key, depsMap.get(ITERATE_KEY))

    // 执行
    let deps = depsMap.get(key)
    deps && deps.forEach(fn => fn.raw())
}

/** 
 * 添加dep依赖
 * @param depsMap {Map} Deps依赖
 * @param key {unknow} 键值
 * @param effects {ReactiveEffect<any> | Set<ReactiveEffect<any>>} 要添加的effect
 */
function addDeps(
    depsMap: DepsMap,
    key: unknown,
    effects: ReactiveEffect<any> | Set<ReactiveEffect<any>>
) {
    let deps = depsMap.get(key)
    !deps && depsMap.set(key, deps = new Set())
    // deps中添加effect
    if (isFunction(effects)) {
        effects.deps.push(deps)
        !deps.has(effects) && deps.add(effects)
    } else {
        effects && effects.forEach(effect => {
            effect.deps.push(deps)
            !deps.has(effect) && deps.add(effect)
        })
    }
}